package com.dsmbase.corp.project.service;

import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.dsmbase.corp.common.Code;
import com.dsmbase.corp.common.DmsUtil;
import com.dsmbase.corp.common.ServiceException;
import com.dsmbase.corp.flow.dao.FlowBranchDao;
import com.dsmbase.corp.flow.dao.FlowDao;
import com.dsmbase.corp.flow.dao.FlowNodeDao;
import com.dsmbase.corp.flow.domain.Flow;
import com.dsmbase.corp.flow.domain.FlowNode;
import com.dsmbase.corp.flow.service.FlowService;
import com.dsmbase.corp.project.dao.ProjectFlowDao;
import com.dsmbase.corp.project.dao.ProjectFlowNodeDao;
import com.dsmbase.corp.project.dao.ProjectIDDao;
import com.dsmbase.corp.project.dao.WorkLoadDao;
import com.dsmbase.corp.project.domain.ProjectFlow;
import com.dsmbase.corp.project.domain.ProjectFlowNode;
import com.dsmbase.corp.project.domain.ProjectID;
import com.dsmbase.corp.project.domain.WorkLoad;
import com.dsmbase.corp.project.web.ProjectForm;
import com.dsmbase.corp.project.web.ProjectNodeForm;
import com.dsmbase.corp.user.domain.User;


import static com.dsmbase.corp.common.FlowUtil.*;

@Component
public class ProjectService {

	@Autowired
	private ProjectFlowDao projectFlowDao;
	
	@Autowired
	private ProjectFlowNodeDao projectFlowNodeDao;
	
	@Autowired
	private FlowDao flowDao;
	
	@Autowired
	private FlowNodeDao flowNodeDao;
	
	@Autowired
	private FlowBranchDao flowBranchDao;
	
	@Autowired
	private FlowService flowService;
	
	@Autowired
	private ProjectIDDao projectIDDao;
	
	@Autowired 
	private WorkLoadDao workLoadDao;
	
	public void createProject(ProjectForm form) throws ServiceException {
		try {
			Flow flow = flowDao.getFlowById(form.getFlowId());
			if (flow == null) {
				throw new ServiceException(Code.INVALID_FLOW_ID);
			}
			FlowNode startNode = null;
			startNode = getStartNode(
					flowNodeDao.getAllFlowNodesByFlowId(flow.getId()),
					flowBranchDao.getFlowBranchByFlowId(flow.getId()));
			ProjectFlow project = new ProjectFlow(flow.getId(), startNode.getId(),
					form.getUserId(), form.getName(), form.getProjectId(), form.getVersion(), new Timestamp(form.getStartTime().getTime()), new Timestamp(form.getEndTime().getTime()),
					form.getComment());
			int newId = projectFlowDao.createProject(project);
			initProjectNodes(newId, form.getNodeList());
		} catch (Exception e) {
			throw new ServiceException(Code.ILLEGAL_PARAM);
		}
	}
	
	private void initProjectNodes(int projectId, List<ProjectNodeForm> nodeList) throws ServiceException {
		try {
			ProjectFlow project = projectFlowDao.getProjectById(projectId);
			Flow flow = flowDao.getFlowById(project.getFlowId());
			List<FlowNode> nodes = flowService.getFlowDefinitionNodes(flow);
			if (nodes.size() != nodeList.size()) {
				throw new ServiceException(Code.ILLEGAL_PARAM);
			}
			for (int i = 0; i < nodes.size(); i++) {
				Timestamp time = project.getStartTime();
				ProjectFlowNode flowNode = new ProjectFlowNode(projectId, nodeList.get(i).getId(), 
						nodeList.get(i).getPlanWorkLoad(), nodeList.get(i).getPlanWorkLoad(), 
						i == 0 ? DmsUtil.ProjectStatus.START.ordinal() : DmsUtil.ProjectStatus.NOT_START.ordinal(),  
						i == 0 ? time : nodeList.get(i).getStartTime(), 
						nodeList.get(i).getEndTime(), nodeList.get(i).getUserId(), 
						DmsUtil.ProxyIndicator.NON_PROXY.ordinal(), i * DmsUtil.WEIGHT_FACTOR);
				projectFlowNodeDao.createProjectNode(flowNode);
			}
		} catch (SQLException e) {
			throw new ServiceException(Code.FAILURE_CREATE_PROJECT_NODE, e);
		}
	}
	
	public List<FlowNode> getTemplateNodes(int flowId) throws ServiceException {
		try {
			return flowNodeDao.getAllFlowNodesByFlowId(flowId);
		} catch (SQLException e) {
			throw new ServiceException(Code.ILLEGAL_PARAM);
		}
	}
	
	public List<Flow> getAllFlow() throws ServiceException {
		List<Flow> flowList = new ArrayList<Flow>();
		try {
			flowList = flowDao.getAllFlow();
		} catch (SQLException e) {
			throw new ServiceException(Code.FLOW_INTERNAL_ERROR);
		}
		return flowList;
	}
	
	public List<ProjectFlow> getAllProjects() {
		List<ProjectFlow> list = new ArrayList<ProjectFlow>();
		try {
			list = projectFlowDao.getAllProjects();
		} catch (SQLException e) {
			return null;
		}
		return list;
	}
	
	public List<ProjectID> getProjectList() {
		try {
			return projectIDDao.getAllProjects();
		} catch (SQLException e) {
			return null;
		}
	}
	
	public ProjectFlow getProjectFlowById(int id) throws ServiceException {
		try {
			ProjectFlow projectFlow = projectFlowDao.getProjectById(id);
			return projectFlow;
		} catch (SQLException e) {
			// TODO log
			throw new ServiceException(Code.ILLEGAL_PARAM);
		}
		
	}
	
	public ProjectForm getProjectFormById(int id) throws ServiceException {
		try {
			ProjectFlow projectFlow = projectFlowDao.getProjectById(id);
			ProjectForm form = new ProjectForm();
			form.setId(projectFlow.getId());
			form.setFlowId(projectFlow.getFlowId());
			form.setName(projectFlow.getName());
			form.setProjectId(projectFlow.getProjectId());
			form.setVersion(projectFlow.getVersion());
			form.setStartTime(projectFlow.getStartTime());
			form.setEndTime(projectFlow.getEndTime());
			form.setComment(projectFlow.getComment());
			List<ProjectFlowNode> nodes = projectFlowNodeDao.getAllProjectNodesByProjectId(projectFlow.getId());
			List<ProjectNodeForm> nodeList = new ArrayList<ProjectNodeForm>();
			for (ProjectFlowNode node : nodes) {
				ProjectNodeForm nodeForm = new ProjectNodeForm();
				nodeForm.setNodeName(flowNodeDao.getFlowNodeById(node.getNodeId()).getName());
				nodeForm.setUserId(node.getUserId());
				nodeForm.setPlanWorkLoad(node.getPlanWorkLoad());
				nodeForm.setActWorkLoad(node.getActWorkLoad());
				nodeForm.setEndTime(node.getEndTime());
				nodeForm.setStartTime(node.getStartTime());
				nodeList.add(nodeForm);
			}
			form.setNodeList(nodeList);
			return form;
		} catch (SQLException e) {
			throw new ServiceException(Code.ILLEGAL_PARAM);
		}
	}
	
	public void updateProject(ProjectForm form) throws ServiceException {
		try {
			ProjectFlow project = projectFlowDao.getProjectById(form.getId());
			project.setStartTime(new Timestamp(form.getStartTime().getTime()));
			project.setEndTime(new Timestamp(form.getEndTime().getTime()));
			project.setComment(form.getComment());
			projectFlowDao.updateProjectFlow(project);
		} catch (SQLException e) {
			throw new ServiceException(Code.ILLEGAL_PARAM);
		}
	}
	
	public void updateProjectNode(ProjectNodeForm nodeForm) throws ServiceException {
		try {
			ProjectFlowNode node = projectFlowNodeDao.getProjectNodeById(nodeForm.getId());
			node.setActWorkLoad(nodeForm.getActWorkLoad());
			Map<Integer, Integer> map = nodeForm.getWorkLoadRecord();
			if (map == null) {
				map = new HashMap<Integer, Integer>();
			}
			int currentMonth = DmsUtil.getCurrentMonth();
			
			if (map.containsKey(currentMonth)) {
				WorkLoad workLoad = workLoadDao.getWorkLoadByKey(nodeForm.getId(), currentMonth); 
				workLoad.setWorkLoad(nodeForm.getCurWorkLoad());
				workLoadDao.updateWorkLoad(workLoad);
			} else {
				WorkLoad workLoad = new WorkLoad();
				workLoad.setMonth(currentMonth);
				workLoad.setProjectNodeId(nodeForm.getId());
				workLoad.setWorkLoad(nodeForm.getCurWorkLoad());
				workLoadDao.insertWorkLoad(workLoad);
			}
			
			List<WorkLoad> workLoads = workLoadDao.getWorkLoadByProjectNodeId(nodeForm.getId());
			int sum = 0;
			for (WorkLoad load : workLoads) {
				sum += load.getWorkLoad();
			}
			if (sum >= nodeForm.getActWorkLoad()) {
				node.setStatus(DmsUtil.ProjectStatus.FINISH.ordinal());
				node.setEndTime(DmsUtil.getCurrentTimestamp());
				ProjectFlowNode nextNode = DmsUtil.getNextProjectNode(projectFlowNodeDao.getAllProjectNodesByProjectId(node.getProjectId()), node);
				if (nextNode != null) {
					nextNode.setStartTime(DmsUtil.getCurrentTimestamp());
					nextNode.setStatus(DmsUtil.ProjectStatus.START.ordinal());
					projectFlowNodeDao.updateProjectNode(nextNode);
				}
			}
			projectFlowNodeDao.updateProjectNode(node);
			
		} catch (SQLException e) {
			// TODO log
			throw new ServiceException(Code.ILLEGAL_PARAM); 
		}
	}
	
	public List<ProjectFlowNode> getAllProjectNodesByUserId(int userId) throws ServiceException {
		try {
			return projectFlowNodeDao.getAllProjectNodesByUserId(userId);
		} catch (SQLException e) {
			// TODO log
			throw new ServiceException(Code.ILLEGAL_PARAM);
		}
	}
	
	public ProjectFlowNode getProjectFlowNode(int id) throws ServiceException {
		try {
			return projectFlowNodeDao.getProjectNodeById(id);
		} catch (SQLException e) {
			// log
			throw new ServiceException(Code.ILLEGAL_PARAM);
		}
	}

	public List<WorkLoad> getWorkLoadByProjectNodeId(int nodeId) throws ServiceException {
		try {
			return workLoadDao.getWorkLoadByProjectNodeId(nodeId);
		} catch (SQLException e) {
			// TODO log
			throw new ServiceException(Code.ILLEGAL_PARAM);
		}
		
	}

	public void handleProxy(ProjectNodeForm nodeForm, User proxyUser) throws ServiceException {
		try {
			ProjectFlowNode node = projectFlowNodeDao.getProjectNodeById(nodeForm.getId());
			if (node.getProxy() == DmsUtil.ProxyIndicator.PROXY.ordinal()) {
				// TODO log
				throw new ServiceException(Code.ILLEGAL_PARAM);
			}
			node.setStatus(DmsUtil.ProjectStatus.PROXY.ordinal());
			projectFlowNodeDao.updateProjectNode(node);
			ProjectFlowNode newNode = new ProjectFlowNode(node.getProjectId(), node.getNodeId(), 
					node.getPlanWorkLoad(), node.getActWorkLoad(), DmsUtil.ProjectStatus.START.ordinal(), 
					DmsUtil.getCurrentTimestamp(), node.getEndTime(), proxyUser.getUserID(), 
					DmsUtil.ProxyIndicator.PROXY.ordinal(), node.getWeight() + 1);
			projectFlowNodeDao.createProjectNode(newNode);
		} catch (SQLException e) {
			// TODO log
			throw new ServiceException(Code.ILLEGAL_PARAM);
		}
		
	}
}
